/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package bean;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import util.StyledBankTransaction;
import Facade.AccountFacadeLocal;
import Facade.BankTransactionFacadeLocal;
import Facade.ManagerLocal;
import entity.Account;
import entity.BankTransaction;
import entity.CurrencyEE;
import exceptions.AccessDeniedException;
import exceptions.ExchangerServiceException;
import exceptions.NotEnoughMoneyException;
import exceptions.AccountException;
import exceptions.TransactionLimitException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.faces.application.FacesMessage;
import javax.faces.component.html.HtmlInputHidden;
import javax.faces.context.FacesContext;
import javax.jms.MapMessage;
import util.JsfUtil;

/**
 *
 * @author Vojtisek
 */
public class BankTransactionManagedBean {

    @EJB
    private ManagerLocal managerBean;
    @Resource(name = "jms/Statement")
    private Queue statement;
    @Resource(name = "jms/StatementFactory")
    private ConnectionFactory statementFactory;
    @EJB
    private AccountFacadeLocal accountFacade;
    @EJB
    private BankTransactionFacadeLocal bankTransactionFacade;
    private Integer toId;
    private String amount;
    private Integer accountId;
    HtmlInputHidden inputHidden;
    private static final Logger logger = Logger.getLogger("BankTransactionManagedBean");
    private CurrencyEE currency;
    private CurrencyEE accountCurrency;
    private BigDecimal balance;

    public String create() {
        if (toId.equals(accountId)) {
            String msg = "Wrong account number.";
            FacesContext.getCurrentInstance().addMessage("form:toId", new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg));
            return null;
        }
        if (accountId == null) {
            JsfUtil.addErrorMessage("Access Denied");
            logger.log(Level.SEVERE, "Create Bank transaction: Access Denied");
            return null;
        }

        try {
            BankTransaction bankTransaction = new BankTransaction();
            bankTransaction.setAmount(BigDecimal.valueOf(Double.parseDouble(amount)));
            bankTransaction.setFrom(bankTransactionFacade.getAccount(accountId));
            bankTransaction.setTo(bankTransactionFacade.getAccount(toId));
            bankTransaction.setCurrency(currency);

            bankTransactionFacade.transfer(bankTransaction);

            JsfUtil.addSuccessMessage("Bank Transaction was successfully created.");
            JsfUtil.addSuccessMessage("From: " + accountId);
            JsfUtil.addSuccessMessage("To: " + toId);
            JsfUtil.addSuccessMessage("Amount: " + amount + " " + currency);
        }catch (TransactionLimitException e) {
            JsfUtil.addErrorMessage("Transaction Limit exceeded");
            JsfUtil.addErrorMessage("Transaction Limit: " + e.getLimit() + " " + currency.name());
            JsfUtil.addErrorMessage("Transfer amount: " + amount + " " + currency.name());
            logger.log(Level.WARNING, null, e);
            return null;
        }  catch (NotEnoughMoneyException e) {
            JsfUtil.addErrorMessage("Not enough Money");
            JsfUtil.addErrorMessage("Account balance: " + e.getBalance() + " " + currency.name());
            JsfUtil.addErrorMessage("Transfer amount: " + amount + " " + currency.name());
            logger.log(Level.WARNING, e.getMessage(), e);
            return null;
        } catch (AccountException e) {
            JsfUtil.addErrorMessage("Transfer error, try it later.");
            logger.log(Level.SEVERE, e.getMessage(), e);
            return null;
//        } catch (EJBException e) {
//            JsfUtil.addErrorMessage("Persistance error: " + e.toString());
//            logger.log(Level.SEVERE, e.getMessage(), e);
//            return null;
        } catch (ExchangerServiceException e) {
            JsfUtil.addErrorMessage("Exchanger Service does not work");
            logger.log(Level.SEVERE, e.getMessage(), e);
            return null;
        } catch (AccessDeniedException e) {
            JsfUtil.addErrorMessage("Access Denied");
            logger.log(Level.SEVERE, e.getMessage(), e);
            return null;
        } catch (Exception ex) {
            JsfUtil.addErrorMessage("Persistance error: " + ex.toString());
            logger.log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public boolean getInitAccountDetails() {
        try {
            if (accountId == null) {
                return false;
            }
            Account account = accountFacade.getAccount(accountId);
            balance = account.getBalance();
            accountCurrency = account.getCurrency();
        } catch (AccessDeniedException e) {
            accountId = null;
            JsfUtil.addErrorMessage("Access Denied");
            logger.log(Level.SEVERE, null, e);
        } catch (Exception ex) {
            logger.log(Level.SEVERE, null, ex);
        }
        return false;
    }

    public List getAllTransactions() {
        if (accountId == null) {
            try {
                List<StyledBankTransaction> foo = new ArrayList<StyledBankTransaction>();
                for (BankTransaction bankTransaction : managerBean.getAllTransactions()) {
                    StyledBankTransaction ff = new StyledBankTransaction(bankTransaction);
                    foo.add(ff);
                }
                Collections.sort(foo);
                return foo;
            } catch (Exception ex) {
                JsfUtil.ensureAddErrorMessage(ex, "Persistance error: " + ex.getMessage());
                logger.log(Level.SEVERE, ex.getMessage(), ex);
                return null;
            }
        } else {
            return getAccountTransactionsManager();
        }
    }

    public List<StyledBankTransaction> getAccountTransactionsManager() {
        if (accountId == null) {
            return null;
        }
        try {
            List<BankTransaction> transactions = managerBean.getAccountTransactions(accountId);
            return getTransactions(transactions);
        } catch (Exception ex) {
            JsfUtil.ensureAddErrorMessage(ex, "Persistance error: " + ex.getMessage());
            logger.log(Level.SEVERE, ex.getMessage(), ex);
            return null;
        }
    }

    public List<StyledBankTransaction> getAccountTransactions() {
        if (accountId == null) {
            return null;
        }
        try {
            List<BankTransaction> transactions = bankTransactionFacade.findAccountTransactions(accountId);
            return getTransactions(transactions);
        } catch (EJBException e) {
            JsfUtil.ensureAddErrorMessage(e, "Access Denied");
            logger.log(Level.SEVERE, e.getMessage(), e);
            return null;
        } catch (Exception ex) {
            JsfUtil.ensureAddErrorMessage(ex, "Persistance error: " + ex.getMessage());
            logger.log(Level.SEVERE, ex.getMessage(), ex);
            return null;
        }
    }

    public Integer getAccountId() {
        return accountId;
    }

    public void setAccountId(Integer accountId) {
        this.accountId = accountId;
    }

    public BigDecimal getBalance() {
        return balance;
    }

    public void setBalance(BigDecimal balance) {
        this.balance = balance;
    }

    public String getAmount() {
        if (amount == null) {
            return amount;
        }

        Double foo = Double.parseDouble(amount);
        if (foo < 0) {
            foo = -foo;
        }

        return foo.toString();
    }

    public void setAmount(String amount) {

        this.amount = amount;
    }

    public Integer getTo() {
        return toId;
    }

    public void setTo(Integer to) {
        this.toId = to;
    }

    public CurrencyEE getCurrency() {
        return currency;
    }

    public void setCurrency(CurrencyEE currency) {
        this.currency = currency;
    }

    public HtmlInputHidden getInputHidden() {
        return inputHidden;
    }

    public void setInputHidden(HtmlInputHidden inputHidden) {
        this.inputHidden = inputHidden;
    }

    public String send() {
        try {
            sendJMSMessageToStatement();
            JsfUtil.addSuccessMessage("Statement was successfully created.");
            logger.log(Level.INFO, "Statement was successfully created.");

        } catch (Exception ex) {
            JsfUtil.ensureAddErrorMessage(ex, "Error: " + ex.toString());
            logger.log(Level.WARNING, "sendToQueue", ex);
        }

        return null;
    }

    private Message createJMSMessageForjmsStatement(Session session) throws JMSException {
//        TextMessage tm = session.createTextMessage();
//        tm.setText(messageData.toString());
//        return tm;
        MapMessage mm = session.createMapMessage();
        mm.setInt("accountId", accountId);
        mm.setLong("time", new Date().getTime());
        return mm;
    }

    private List<StyledBankTransaction> getTransactions(List<BankTransaction> transactions) {
        List<StyledBankTransaction> foo = new ArrayList<StyledBankTransaction>();
        for (BankTransaction bankTransaction : transactions) {
            if (bankTransaction.getFrom().getId().equals(accountId)) {
                StyledBankTransaction ff = new StyledBankTransaction(bankTransaction);
                ff.setStyleClass("sended");
                ff.setContraAccount(bankTransaction.getTo().getId().toString());
                ff.setCustomerAmount(bankTransaction.getAmountFrom().negate());
                ff.setAmount(ff.getAmount().negate());
                ff.setRender(true);
                foo.add(ff);
            } else {
                StyledBankTransaction ff = new StyledBankTransaction(bankTransaction);
                ff.setContraAccount(bankTransaction.getFrom().getId().toString());
                ff.setCustomerAmount(ff.getAmountTo());
                ff.setStyleClass("received");
                foo.add(ff);
            }
        }
        Collections.sort(foo);
        return foo;
    }

    private void sendJMSMessageToStatement() throws JMSException {
        Connection connection = null;
        Session session = null;
        try {
            connection = statementFactory.createConnection();
            session =
                    connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            MessageProducer messageProducer = session.createProducer(statement);
            messageProducer.send(createJMSMessageForjmsStatement(session));
        } finally {
            if (session != null) {
                try {
                    session.close();
                } catch (JMSException e) {
                    logger.log(Level.WARNING, "Cannot close session", e);
                }

            }
            if (connection != null) {
                connection.close();
            }
        }
    }



    public CurrencyEE getAccountCurrency() {
        return accountCurrency;
    }

    public void setAccountCurrency(CurrencyEE accountCurrency) {
        this.accountCurrency = accountCurrency;
    }
}
